home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
HAM Radio 3.2
/
Ham Radio Version 3.2 (Chestnut CD-ROMs)(1993).ISO
/
packet
/
n17jsrc
/
socket.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-01-27
|
47KB
|
1,984 lines
/* Application programming interface routines - based loosely on the
* "socket" model in Berkeley UNIX.
*
* Copyright 1991 Phil Karn, KA9Q
*/
#include <stdio.h>
#ifdef __STDC__
#include <stdarg.h>
#endif
#include "global.h"
#include "mbuf.h"
#include "netuser.h"
#include "timer.h"
#include "iface.h"
#include "ip.h"
#include "tcp.h"
#include "udp.h"
#include "ax25.h"
#include "lapb.h"
#include "netrom.h"
#include "nr4.h"
#include "proc.h"
#include "lzw.h"
#include "usock.h"
#include "socket.h"
#include "config.h"
static void autobind __ARGS((int s,int af));
static int checkaddr __ARGS((int type,char *name,int namelen));
static void rip_recv __ARGS((struct raw_ip *rp));
static void s_trcall __ARGS((struct tcb *tcb,int cnt));
static void s_tscall __ARGS((struct tcb *tcb,int old,int new));
static void s_ttcall __ARGS((struct tcb *tcb,int cnt));
static void s_urcall __ARGS((struct iface *iface,struct udp_cb *udp,int cnt));
static void trdiscard __ARGS((struct tcb *tcb,int cnt));
#ifdef NETROM
static void s_nrcall __ARGS((struct nr4cb *cb,int cnt));
static void s_nscall __ARGS((struct nr4cb *cb,int old,int new));
static void s_ntcall __ARGS((struct nr4cb *cb,int cnt));
#endif
static int16 Lport = 1024;
char *Socktypes[] = {
"Not Used",
"TCP",
"UDP",
"AX25 I",
"AX25 UI",
"Raw IP",
"NETROM3",
"NETROM",
"Loc St",
"Loc Dg"
};
char Badsocket[] = "Bad socket";
struct usock *Usock; /* Socket entry array */
int Nusock = DEFNSOCK; /* Number of socket entries */
/* The following two variables are needed because there can be only one
* socket listening on each of the AX.25 modes (I and UI)
*/
#ifdef AX25
int Axi_sock = -1; /* Socket number listening for AX25 connections */
static int Axui_sock = -1; /* Socket number listening for AX25 UI frames */
static struct mbuf *Bcq; /* Queue of incoming UI frames */
/* Function that handles incoming UI frames from lapb.c */
void
beac_input(iface,src,bp)
struct iface *iface;
char *src;
struct mbuf *bp;
{
struct mbuf *hdr;
struct sockaddr_ax *sax;
if(Axui_sock == -1){
/* Nobody there to read it */
free_p(bp);
} else {
if((hdr = pushdown(NULLBUF,sizeof(struct sockaddr_ax))) == NULLBUF){
free_p(bp);
return;
}
sax = (struct sockaddr_ax *)hdr->data;
sax->sax_family = AF_AX25;
memcpy(sax->ax25_addr,src,AXALEN);
strncpy(sax->iface,iface->name,ILEN);
hdr->next = bp;
enqueue(&Bcq,hdr);
}
}
#endif
/* Initialize user socket array */
void
sockinit()
{
if(Usock != NULLUSOCK)
return; /* Already initialized */
Usock = (struct usock *)callocw(Nusock,sizeof(struct usock));
}
/* Create a user socket, return socket index
* The mapping to actual protocols is as follows:
*
*
* ADDRESS FAMILY Stream Datagram Raw Seq. Packet
*
* AF_INET TCP UDP IP
* AF_AX25 I-frames UI-frames
* AF_NETROM NET/ROM L3 NET/ROM L4
* AF_LOCAL stream loopback packet loopback
*/
int
socket(af,type,protocol)
int af; /* Address family */
int type; /* Stream or datagram */
int protocol; /* Used for raw IP sockets */
{
register struct usock *up;
int s;
for(up=Usock;up < &Usock[Nusock];up++)
if(up->type == NOTUSED)
break;
if(up == &Usock[Nusock]){
/* None left */
errno = EMFILE;
return -1;
}
up->refcnt = 1;
s = up - Usock + SOCKBASE;
errno = 0;
up->noblock = 0;
up->rdysock = -1;
up->cb.p = NULLCHAR;
up->peername = up->name = NULLCHAR;
up->namelen = up->peernamelen = 0;
up->owner = Curproc;
up->obuf = NULLBUF;
memset(up->errcodes,0,sizeof(up->errcodes));
memset(up->eol,0,sizeof(up->eol));
up->flush = '\n'; /* default is line buffered */
switch(af){
case AF_LOCAL:
up->cb.local = (struct loc *) callocw(1,sizeof(struct loc));
up->cb.local->peer = up; /* connect to self */
switch(type){
case SOCK_STREAM:
up->type = TYPE_LOCAL_STREAM;
up->cb.local->hiwat = LOCSFLOW;
break;
case SOCK_DGRAM:
up->type = TYPE_LOCAL_DGRAM;
up->cb.local->hiwat = LOCDFLOW;
break;
default:
free(up->cb.local);
errno = ESOCKTNOSUPPORT;
break;
}
break;
#ifdef AX25
case AF_AX25:
switch(type){
case SOCK_STREAM:
up->type = TYPE_AX25I;
break;
case SOCK_DGRAM:
up->type = TYPE_AX25UI;
break;
default:
errno = ESOCKTNOSUPPORT;
break;
}
strcpy(up->eol,AX_EOL);
break;
#endif
#ifdef NETROM
case AF_NETROM:
switch(type){
case SOCK_RAW:
up->type = TYPE_NETROML3;
up->cb.rnr = raw_nr((char)protocol);
break;
case SOCK_SEQPACKET:
up->type = TYPE_NETROML4;
break;
default:
errno = ESOCKTNOSUPPORT;
break;
}
strcpy(up->eol,AX_EOL);
break;
#endif
case AF_INET:
switch(type){
case SOCK_STREAM:
up->type = TYPE_TCP;
strcpy(up->eol,INET_EOL);
break;
case SOCK_DGRAM:
up->type = TYPE_UDP;
break;
case SOCK_RAW:
up->type = TYPE_RAW;
up->cb.rip = raw_ip(protocol,rip_recv);
up->cb.rip->user = s;
break;
default:
errno = ESOCKTNOSUPPORT;
break;
}
break;
default:
errno = EAFNOSUPPORT;
break;
}
if(errno)
return -1;
return s;
}
/* Attach a local address/port to a socket. If not issued before a connect
* or listen, will be issued automatically
*/
int
bind(s,name,namelen)
int s; /* Socket index */
char *name; /* Local name */
int namelen; /* Length of name */
{
register struct usock *up;
union sp local;
struct socket lsock;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return -1;
}
if(up->type == TYPE_LOCAL_STREAM || up->type == TYPE_LOCAL_DGRAM){
errno = EINVAL;
return -1;
}
if(name == NULLCHAR){
errno = EFAULT;
return -1;
}
if(up->name != NULLCHAR){
/* Bind has already been issued */
errno = EINVAL;
return -1;
}
if(checkaddr(up->type,name,namelen) == -1){
/* Incorrect length or family for chosen protocol */
errno = EAFNOSUPPORT;
return -1;
}
/* Stash name in an allocated block */
up->namelen = namelen;
up->name = mallocw(namelen);
memcpy(up->name,name,namelen);
/* Create control block for datagram sockets */
switch(up->type){
case TYPE_UDP:
local.in = (struct sockaddr_in *)up->name;
lsock.address = local.in->sin_addr.s_addr;
lsock.port = local.in->sin_port;
up->cb.udp = open_udp(&lsock,s_urcall);
up->cb.udp->user = s;
break;
#ifdef AX25
case TYPE_AX25UI:
if(Axui_sock != -1){
errno = EADDRINUSE;
return -1;
}
Axui_sock = s;
break;
#endif
}
return 0;
}
/* Post a listen on a socket */
int
listen(s,backlog)
int s; /* Socket index */
int backlog; /* 0 for a single connection, 1 for multiple connections */
{
register struct usock *up;
union sp local;
struct socket lsock;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return -1;
}
if(up->type == TYPE_LOCAL_STREAM || up->type == TYPE_LOCAL_DGRAM){
errno = EINVAL;
return -1;
}
if(up->cb.p != NULLCHAR){
errno = EISCONN;
return -1;
}
switch(up->type){
case TYPE_TCP:
if(up->name == NULLCHAR)
autobind(s,AF_INET);
local.in = (struct sockaddr_in *)up->name;
lsock.address = local.in->sin_addr.s_addr;
lsock.port = local.in->sin_port;
up->cb.tcb = open_tcp(&lsock,NULLSOCK,
backlog ? TCP_SERVER:TCP_PASSIVE,0,
s_trcall,s_ttcall,s_tscall,0,s);
break;
#ifdef AX25
case TYPE_AX25I:
if(up->name == NULLCHAR)
autobind(s,AF_AX25);
if(s != Axi_sock){
errno = EOPNOTSUPP;
return -1;
}
local.ax = (struct sockaddr_ax *)up->name;
up->cb.ax25 = open_ax25(NULLIF,local.ax->ax25_addr,NULLCHAR,
backlog ? AX_SERVER:AX_PASSIVE,0,
s_arcall,s_atcall,s_ascall,s);
break;
#endif
#ifdef NETROM
case TYPE_NETROML4:
if(up->name == NULLCHAR)
autobind(s,AF_NETROM);
local.nr = (struct sockaddr_nr *)up->name;
up->cb.nr4 = open_nr4(&local.nr->nr_addr,NULLNRADDR,
backlog ? AX_SERVER:AX_PASSIVE,
s_nrcall,s_ntcall,s_nscall,s);
break;
#endif
default:
/* Listen not supported on datagram sockets */
errno = EOPNOTSUPP;
return -1;
}
return 0;
}
/* Initiate active open. For datagram sockets, merely bind the remote address. */
int
connect(s,peername,peernamelen)
int s; /* Socket index */
char *peername; /* Peer name */
int peernamelen; /* Length of peer name */
{
register struct usock *up;
union cb cb;
union sp local,remote;
struct socket lsock,fsock;
#ifdef AX25
struct iface *iface;
#endif
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return -1;
}
if(up->type == TYPE_LOCAL_DGRAM || up->type == TYPE_LOCAL_STREAM){
errno = EINVAL;
return -1;
}
if(peername == NULLCHAR){
/* Connect must specify a remote address */
errno = EFAULT;
return -1;
}
if(checkaddr(up->type,peername,peernamelen) == -1){
errno = EAFNOSUPPORT;
return -1;
}
/* Raw socket control blocks are created in socket() */
if(up->type != TYPE_RAW && up->type != TYPE_NETROML3 &&
up->cb.p != NULLCHAR){
errno = EISCONN;
return -1;
}
up->peername = mallocw(peernamelen);
memcpy(up->peername,peername,peernamelen);
up->peernamelen = peernamelen;
/* Set up the local socket structures */
if(up->name == NULLCHAR){
switch(up->type){
case TYPE_TCP:
case TYPE_UDP:
case TYPE_RAW:
autobind(s,AF_INET);
break;
#ifdef AX25
case TYPE_AX25I:
case TYPE_AX25UI:
autobind(s,AF_AX25);
break;
#endif
#ifdef NETROM
case TYPE_NETROML3:
case TYPE_NETROML4:
autobind(s,AF_NETROM);
break;
#endif
}
}
switch(up->type){
case TYPE_TCP:
/* Construct the TCP-style ports from the sockaddr structs */
local.in = (struct sockaddr_in *)up->name;
remote.in = (struct sockaddr_in *)up->peername;
if(local.in->sin_addr.s_addr == INADDR_ANY)
/* Choose a local address */
local.in->sin_addr.s_addr =
locaddr(remote.in->sin_addr.s_addr);
lsock.address = local.in->sin_addr.s_addr;
lsock.port = local.in->sin_port;
fsock.address = remote.in->sin_addr.s_addr;
fsock.port = remote.in->sin_port;
/* Open the TCB in active mode */
up->cb.tcb = open_tcp(&lsock,&fsock,TCP_ACTIVE,0,
s_trcall,s_ttcall,s_tscall,0,s);
/* Wait for the connection to complete */
while((cb.tcb = up->cb.tcb) != NULLTCB && cb.tcb->state != TCP_ESTABLISHED){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if((errno = pwait(up)) != 0){
return -1;
}
}
if(cb.tcb == NULLTCB){
/* Probably got refused */
free(up->peername);
up->peername = NULLCHAR;
errno = ECONNREFUSED;
return -1;
}
break;
case TYPE_UDP:
#ifdef AX25
case TYPE_AX25UI:
#endif
#ifdef NETROM
case TYPE_NETROML3:
#endif
case TYPE_RAW:
/* Control block already created by bind() */
break;
#ifdef AX25
case TYPE_AX25I:
local.ax = (struct sockaddr_ax *)up->name;
remote.ax = (struct sockaddr_ax *)up->peername;
if((iface = if_lookup(remote.ax->iface)) == NULLIF){
errno = EINVAL;
return -1;
}
if(local.ax->ax25_addr[0] == '\0'){
/* The local address was unspecified; set it from
* the interface address
*/
memcpy(local.ax->ax25_addr,iface->hwaddr,AXALEN);
}
/* If we already have an AX25 link we can use it */
if((up->cb.ax25 = find_ax25(remote.ax->ax25_addr)) != NULLAX25
&& up->cb.ax25->state != LAPB_DISCONNECTED &&
up->cb.ax25->user == -1) {
up->cb.ax25->user = s;
up->cb.ax25->r_upcall = s_arcall;
up->cb.ax25->t_upcall = s_atcall;
up->cb.ax25->s_upcall = s_ascall;
if(up->cb.ax25->state == LAPB_CONNECTED
|| up->cb.ax25->state == LAPB_RECOVERY)
return 0;
} else
up->cb.ax25 = open_ax25(iface,local.ax->ax25_addr,
remote.ax->ax25_addr,AX_ACTIVE,
Axwindow,s_arcall,s_atcall,s_ascall,s);
/* Wait for the connection to complete */
while((cb.ax25 = up->cb.ax25) != NULLAX25 && cb.ax25->state != LAPB_CONNECTED){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if((errno = pwait(up)) != 0){
return -1;
}
}
if(cb.ax25 == NULLAX25){
/* Connection probably already exists */
free(up->peername);
up->peername = NULLCHAR;
errno = ECONNREFUSED;
return -1;
}
break;
#endif
#ifdef NETROM
case TYPE_NETROML4:
local.nr = (struct sockaddr_nr *)up->name;
remote.nr = (struct sockaddr_nr *)up->peername;
up->cb.nr4 = open_nr4(&local.nr->nr_addr,&remote.nr->nr_addr,
AX_ACTIVE,s_nrcall,s_ntcall,s_nscall,s);
/* Wait for the connection to complete */
while((cb.nr4 = up->cb.nr4) != NULLNR4CB && cb.nr4->state != NR4STCON){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if((errno = pwait(up)) != 0){
return -1;
}
}
if(cb.nr4 == NULLNR4CB){
/* Connection probably already exists */
free(up->peername);
up->peername = NULLCHAR;
errno = ECONNREFUSED;
return -1;
}
break;
#endif
}
return 0;
}
/* Wait for a connection. Valid only for connection-oriented sockets. */
int
accept(s,peername,peernamelen)
int s; /* Socket index */
char *peername; /* Peer name */
int *peernamelen; /* Length of peer name */
{
int i;
register struct usock *up;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return -1;
}
if(up->type == TYPE_LOCAL_DGRAM || up->type == TYPE_LOCAL_STREAM){
errno = EINVAL;
return -1;
}
if(up->cb.p == NULLCHAR){
errno = EOPNOTSUPP;
return -1;
}
/* Accept is valid only for stream sockets */
switch(up->type){
case TYPE_TCP:
#ifdef AX25
case TYPE_AX25I:
#endif
#ifdef NETROM
case TYPE_NETROML4:
#endif
break;
default:
errno = EOPNOTSUPP;
return -1;
}
/* Wait for the state-change upcall routine to signal us */
while(up->cb.p != NULLCHAR && up->rdysock == -1){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if((errno = pwait(up)) != 0){
return -1;
}
}
if(up->cb.p == NULLCHAR){
/* Blown away */
errno = EBADF;
return -1;
}
i = up->rdysock;
up->rdysock = -1;
up = itop(i);
if(peername != NULLCHAR && peernamelen != NULL){
*peernamelen = min(up->peernamelen,*peernamelen);
memcpy(peername,up->peername,*peernamelen);
}
return i;
}
/* Low-level receive routine. Passes mbuf back to user; more efficient than
* higher-level functions recv() and recvfrom(). Datagram sockets ignore
* the len parameter.
*/
int
recv_mbuf(s,bpp,flags,from,fromlen)
int s; /* Socket index */
struct mbuf **bpp; /* Place to stash receive buffer */
int flags; /* Unused; will control out-of-band data, etc */
char *from; /* Peer address (only for datagrams) */
int *fromlen; /* Length of peer address */
{
register struct usock *up;
int cnt;
union cb cb;
struct socket fsocket;
#ifdef NETROM
struct nr3hdr n3hdr;
#endif
union sp remote;
struct ip ip;
struct mbuf *bp;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return -1;
}
if(up->ibuf != NULLBUF){
/* Return input buffer */
cnt = len_p(up->ibuf);
if(bpp != NULLBUFP)
*bpp = up->ibuf;
else
free_p(up->ibuf);
up->ibuf = NULLBUF;
return cnt;
}
switch(up->type){
case TYPE_LOCAL_STREAM:
case TYPE_LOCAL_DGRAM:
while(up->cb.local != NULLLOC && up->cb.local->q == NULLBUF
&& up->cb.local->peer != NULLUSOCK){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if((errno = pwait(up)) != 0){
return -1;
}
}
if(up->cb.local == NULLLOC){
errno = EBADF;
return -1;
}
if(up->cb.local->q == NULLBUF &&
up->cb.local->peer == NULLUSOCK){
errno = ENOTCONN;
return -1;
}
/* For datagram sockets, this will return the
* first packet on the queue. For stream sockets,
* this will return everything.
*/
bp = dequeue(&up->cb.local->q);
if(up->cb.local->q == NULLBUF
&& (up->cb.local->flags & LOC_SHUTDOWN))
close_s(s);
psignal(up,0);
cnt = len_p(bp);
break;
case TYPE_TCP:
while((cb.tcb = up->cb.tcb) != NULLTCB
&& cb.tcb->r_upcall != trdiscard
&& (cnt = recv_tcp(cb.tcb,&bp,(int16)0)) == -1){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if((errno = pwait(up)) != 0){
return -1;
}
}
if(cb.tcb == NULLTCB){
/* Connection went away */
errno = ENOTCONN;
return -1;
} else if(cb.tcb->r_upcall == trdiscard){
/* Receive shutdown has been done */
errno = ENOTCONN; /* CHANGE */
return -1;
}
break;
case TYPE_UDP:
while((cb.udp = up->cb.udp) != NULLUDP
&& (cnt = recv_udp(cb.udp,&fsocket,&bp)) == -1){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if((errno = pwait(up)) != 0){
return -1;
}
}
if(cb.udp == NULLUDP){
/* Connection went away */
errno = ENOTCONN;
return -1;
}
if(from != NULLCHAR && fromlen != (int *)NULL && *fromlen >= SOCKSIZE){
remote.in = (struct sockaddr_in *)from;
remote.in->sin_family = AF_INET;
remote.in->sin_addr.s_addr = fsocket.address;
remote.in->sin_port = fsocket.port;
*fromlen = SOCKSIZE;
}
break;
case TYPE_RAW:
while((cb.rip = up->cb.rip) != NULLRIP
&& cb.rip->rcvq == NULLBUF){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if((errno = pwait(up)) != 0){
return -1;
}
}
if(cb.rip == NULLRIP){
/* Connection went away */
errno = ENOTCONN;
return -1;
}
bp = dequeue(&cb.rip->rcvq);
ntohip(&ip,&bp);
cnt = len_p(bp);
if(from != NULLCHAR && fromlen != (int *)NULL && *fromlen >= SOCKSIZE){
remote.in = (struct sockaddr_in *)from;
remote.in->sin_family = AF_INET;
remote.in->sin_addr.s_addr = ip.source;
remote.in->sin_port = 0;
*fromlen = SOCKSIZE;
}
break;
#ifdef AX25
case TYPE_AX25I:
while((cb.ax25 = up->cb.ax25) != NULLAX25
&& (bp = recv_ax25(cb.ax25,(int16)0)) == NULLBUF){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if((errno = pwait(up)) != 0){
return -1;
}
}
if(cb.ax25 == NULLAX25){
/* Connection went away */
errno = ENOTCONN;
return -1;
}
cnt = bp->cnt;
break;
case TYPE_AX25UI:
while(s == Axui_sock && Bcq == NULLBUF){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if((errno = pwait(&Bcq)) != 0){
return -1;
}
}
if(s != Axui_sock){
errno = ENOTCONN;
return -1;
}
bp = dequeue(&Bcq);
if(from != NULLCHAR && fromlen != NULLINT
&& *fromlen >= sizeof(struct sockaddr_ax)){
pullup(&bp,from,sizeof(struct sockaddr_ax));
*fromlen = sizeof(struct sockaddr_ax);
} else {
pullup(&bp,NULLCHAR,sizeof(struct sockaddr_ax));
}
cnt = len_p(bp);
break;
#endif
#ifdef NETROM
case TYPE_NETROML3:
while((cb.rnr = up->cb.rnr) != NULLRNR
&& cb.rnr->rcvq == NULLBUF){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if((errno = pwait(up)) != 0){
return -1;
}
}
if(cb.rnr == NULLRNR){
/* Connection went away */
errno = ENOTCONN;
return -1;
}
bp = dequeue(&cb.rnr->rcvq);
ntohnr3(&n3hdr,&bp);
cnt = len_p(bp);
if(from != NULLCHAR && fromlen != NULLINT
&& *fromlen >= sizeof(struct sockaddr_nr)){
remote.nr = (struct sockaddr_nr *)from;
remote.nr->nr_family = AF_NETROM;
/* The callsign of the local user is not part of
NET/ROM level 3, so that field is not used here */
memcpy(remote.nr->nr_addr.node,n3hdr.source,AXALEN);
*fromlen = sizeof(struct sockaddr_nr);
}
break;
case TYPE_NETROML4:
while((cb.nr4 = up->cb.nr4) != NULLNR4CB
&& (bp = recv_nr4(cb.nr4,(int16)0)) == NULLBUF){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if((errno = pwait(up)) != 0){
return -1;
}
}
if(cb.nr4 == NULLNR4CB){
/* Connection went away */
errno = ENOTCONN;
return -1;
}
cnt = bp->cnt;
break;
#endif
}
if(bpp != NULLBUFP)
*bpp = bp;
else
free_p(bp);
return cnt;
}
/* Low level send routine; user supplies mbuf for transmission. More
* efficient than send() or sendto(), the higher level interfaces.
* The "to" and "tolen" parameters are ignored on connection-oriented
* sockets.
*
* In case of error, bp is freed so the caller doesn't have to worry about it.
*/
int
send_mbuf(s,bp,flags,to,tolen)
int s; /* Socket index */
struct mbuf *bp; /* Buffer to send */
int flags; /* not currently used */
char *to; /* Destination, only for datagrams */
int tolen; /* Length of destination */
{
register struct usock *up;
union cb cb;
union sp local,remote;
struct socket lsock,fsock;
int cnt;
if((up = itop(s)) == NULLUSOCK){
free_p(bp);
errno = EBADF;
return -1;
}
#ifndef notdef
if(up->obuf != NULLBUF){
/* If there's unflushed output, send it.
* Note the importance of clearing up->obuf
* before the recursive call!
*/
struct mbuf *bp1;
bp1 = up->obuf;
up->obuf = NULLBUF;
send_mbuf(s,bp1,flags,to,tolen);
}
#endif
if(up->name == NULLCHAR){
/* Automatic local name assignment for datagram sockets */
switch(up->type){
case TYPE_LOCAL_STREAM:
case TYPE_LOCAL_DGRAM:
break; /* no op */
case TYPE_UDP:
case TYPE_RAW:
autobind(s,AF_INET);
break;
#ifdef AX25
case TYPE_AX25UI:
autobind(s,AF_AX25);
break;
#endif
#ifdef NETROM
case TYPE_NETROML3:
case TYPE_NETROML4:
autobind(s,AF_NETROM);
break;
#endif
default:
free_p(bp);
errno = ENOTCONN;
return -1;
}
}
cnt = len_p(bp);
switch(up->type){
case TYPE_LOCAL_DGRAM:
if(up->cb.local->peer == NULLUSOCK){
free_p(bp);
errno = ENOTCONN;
return -1;
}
enqueue(&up->cb.local->peer->cb.local->q,bp);
psignal(up->cb.local->peer,0);
/* If high water mark has been reached, block */
while(up->cb.local->peer != NULLUSOCK &&
len_q(up->cb.local->peer->cb.local->q) >=
up->cb.local->peer->cb.local->hiwat){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if((errno = pwait(up->cb.local->peer)) != 0){
return -1;
}
}
if(up->cb.local->peer == NULLUSOCK){
errno = ENOTCONN;
return -1;
}
break;
case TYPE_LOCAL_STREAM:
if(up->cb.local->peer == NULLUSOCK){
free_p(bp);
errno = ENOTCONN;
return -1;
}
append(&up->cb.local->peer->cb.local->q,bp);
psignal(up->cb.local->peer,0);
/* If high water mark has been reached, block */
while(up->cb.local->peer != NULLUSOCK &&
len_p(up->cb.local->peer->cb.local->q) >=
up->cb.local->peer->cb.local->hiwat){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if((errno = pwait(up->cb.local->peer)) != 0){
return -1;
}
}
if(up->cb.local->peer == NULLUSOCK){
errno = ENOTCONN;
return -1;
}
break;
case TYPE_TCP:
if((cb.tcb = up->cb.tcb) == NULLTCB){
free_p(bp);
errno = ENOTCONN;
return -1;
}
cnt = send_tcp(cb.tcb,bp);
while((cb.tcb = up->cb.tcb) != NULLTCB &&
cb.tcb->sndcnt > cb.tcb->window){
/* Send queue is full */
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if((errno = pwait(up)) != 0){
return -1;
}
}
if(cb.tcb == NULLTCB){
errno = ENOTCONN;
return -1;
}
break;
case TYPE_UDP:
local.in = (struct sockaddr_in *)up->name;
lsock.address = local.in->sin_addr.s_addr;
lsock.port = local.in->sin_port;
if(to != NULLCHAR)
remote.in = (struct sockaddr_in *)to;
else if(up->peername != NULLCHAR)
remote.in = (struct sockaddr_in *)up->peername;
else {
errno = ENOTCONN;
return -1;
}
fsock.address = remote.in->sin_addr.s_addr;
fsock.port = remote.in->sin_port;
send_udp(&lsock,&fsock,0,0,bp,0,0,0);
break;
case TYPE_RAW:
local.in = (struct sockaddr_in *)up->name;
if(to != NULLCHAR)
remote.in = (struct sockaddr_in *)to;
else if(up->peername != NULLCHAR)
remote.in = (struct sockaddr_in *)up->peername;
else {
errno = ENOTCONN;
return -1;
}
ip_send(local.in->sin_addr.s_addr,remote.in->sin_addr.s_addr,
(char)up->cb.rip->protocol,0,0,bp,0,0,0);
break;
#ifdef AX25
case TYPE_AX25I:
if((cb.ax25 = up->cb.ax25) == NULLAX25){
free_p(bp);
errno = ENOTCONN;
return -1;
}
send_ax25(cb.ax25,bp,PID_NO_L3);
while((cb.ax25 = up->cb.ax25) != NULLAX25 &&
len_q(cb.ax25->txq) * cb.ax25->paclen > cb.ax25->window){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if((errno = pwait(up)) != 0){
return -1;
}
}
if(cb.ax25 == NULLAX25){
errno = EBADF;
return -1;
}
break;
case TYPE_AX25UI:
local.ax = (struct sockaddr_ax *)up->name;
if(to != NULLCHAR)
remote.ax = (struct sockaddr_ax *)to;
else if(up->peername != NULLCHAR)
remote.ax = (struct sockaddr_ax *)up->peername;
else {
errno = ENOTCONN;
return -1;
}
ax_output(if_lookup(remote.ax->iface),
remote.ax->ax25_addr,
local.ax->ax25_addr,PID_NO_L3,bp);
break;
#endif
#ifdef NETROM
case TYPE_NETROML3:
if(len_p(bp) > NR4MAXINFO) {
free_p(bp);
errno = EMSGSIZE;
return -1;
}
local.nr = (struct sockaddr_nr *)up->name;
if(to != NULLCHAR)
remote.nr = (struct sockaddr_nr *)to;
else if(up->peername != NULLCHAR)
remote.nr = (struct sockaddr_nr *)up->peername;
else {
errno = ENOTCONN;
return -1;
}
/* The NETROM username is always ignored in outgoing traffic */
nr_sendraw(remote.nr->nr_addr.node,
up->cb.rnr->protocol,up->cb.rnr->protocol,bp);
break;
case TYPE_NETROML4:
if((cb.nr4 = up->cb.nr4) == NULLNR4CB) {
free_p(bp);
errno = ENOTCONN;
return -1;
}
if(len_p(bp) > NR4MAXINFO){ /* reject big packets */
free_p(bp);
errno = EMSGSIZE;
return -1;
}
send_nr4(cb.nr4,bp);
while((cb.nr4 = up->cb.nr4) != NULLNR4CB &&
cb.nr4->nbuffered >= cb.nr4->window){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if((errno = pwait(up)) != 0){
errno = EINTR;
return -1;
}
}
if(cb.nr4 == NULLNR4CB){
errno = EBADF;
return -1;
}
break;
#endif
}
return cnt;
}
/* Return local name passed in an earlier bind() call */
int
getsockname(s,name,namelen)
int s; /* Socket index */
char *name; /* Place to stash name */
int *namelen; /* Length of same */
{
register struct usock *up;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return -1;
}
if(name == NULLCHAR || namelen == (int *)NULL){
errno = EFAULT;
return -1;
}
if(up->name == NULLCHAR){
/* Not bound yet */
*namelen = 0;
return 0;
}
if(up->name != NULLCHAR){
*namelen = min(*namelen,up->namelen);
memcpy(name,up->name,*namelen);
}
return 0;
}
/* Get remote name, returning result of earlier connect() call. */
int
getpeername(s,peername,peernamelen)
int s; /* Socket index */
char *peername; /* Place to stash name */
int *peernamelen; /* Length of same */
{
register struct usock *up;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return -1;
}
if(up->peername == NULLCHAR){
errno = ENOTCONN;
return -1;
}
if(peername == NULLCHAR || peernamelen == (int *)NULL){
errno = EFAULT;
return -1;
}
*peernamelen = min(*peernamelen,up->peernamelen);
memcpy(peername,up->peername,*peernamelen);
return 0;
}
/* Return length of protocol queue, either send or receive. */
int
socklen(s,rtx)
int s; /* Socket index */
int rtx; /* 0 = receive queue, 1 = transmit queue */
{
register struct usock *up;
int len = -1;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return -1;
}
if(up->cb.p == NULLCHAR){
errno = ENOTCONN;
return -1;
}
if(rtx < 0 || rtx > 1){
errno = EINVAL;
return -1;
}
switch(up->type){
case TYPE_LOCAL_DGRAM:
switch(rtx){
case 0:
len = len_q(up->cb.local->q);
break;
case 1:
if(up->cb.local->peer != NULLUSOCK)
len = len_q(up->cb.local->peer->cb.local->q);
break;
}
break;
case TYPE_LOCAL_STREAM:
switch(rtx){
case 0:
len = len_p(up->cb.local->q) + len_p(up->ibuf);
break;
case 1:
if(up->cb.local->peer != NULLUSOCK)
len = len_p(up->cb.local->peer->cb.local->q)
+ len_p(up->obuf);
break;
}
break;
case TYPE_TCP:
switch(rtx){
case 0:
len = up->cb.tcb->rcvcnt + len_p(up->ibuf);
break;
case 1:
len = up->cb.tcb->sndcnt + len_p(up->obuf);
break;
}
break;
case TYPE_UDP:
switch(rtx){
case 0:
len = up->cb.udp->rcvcnt;
break;
case 1:
len = 0;
break;
}
break;
#ifdef AX25
case TYPE_AX25I:
switch(rtx){
case 0:
len = len_p(up->cb.ax25->rxq) + len_p(up->ibuf);
break;
case 1: /* Number of packets, not bytes */
len = len_q(up->cb.ax25->txq);
if(up->obuf != NULLBUF)
len++;
}
break;
case TYPE_AX25UI:
switch(rtx){
case 0:
len = len_q(Bcq);
break;
case 1:
len = 0;
break;
}
break;
#endif
#ifdef NETROM
case TYPE_NETROML3:
switch(rtx){
case 0:
len = len_q(up->cb.rnr->rcvq);
break;
case 1:
len = 0;
}
break;
case TYPE_NETROML4:
switch(rtx){
case 0:
len = len_p(up->cb.nr4->rxq) + len_p(up->obuf);
break;
case 1: /* Number of packets, not bytes */
len = len_q(up->cb.nr4->txq);
if(up->obuf != NULLBUF)
len++;
break;
}
break;
#endif
case TYPE_RAW:
switch(rtx){
case 0:
len = len_q(up->cb.rip->rcvq);
break;
case 1:
len = 0;
break;
}
break;
}
return len;
}
/* Force retransmission. Valid only for connection-oriented sockets. */
int
sockkick(s)
int s; /* Socket index */
{
register struct usock *up;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return -1;
}
if(up->type == TYPE_LOCAL_STREAM || up->type == TYPE_LOCAL_DGRAM){
errno = EINVAL;
return -1;
}
if(up->cb.p == NULLCHAR){
errno = ENOTCONN;
return -1;
}
switch(up->type){
case TYPE_TCP:
kick_tcp(up->cb.tcb);
break;
#ifdef AX25
case TYPE_AX25I:
kick_ax25(up->cb.ax25);
break;
#endif
#ifdef NETROM
case TYPE_NETROML4:
kick_nr4(up->cb.nr4);
break;
#endif
default:
/* Datagram sockets can't be kicked */
errno = EOPNOTSUPP;
return -1;
}
return 0;
}
/* Change owner of socket, return previous owner */
struct proc *
sockowner(s,newowner)
int s; /* Socket index */
struct proc *newowner; /* Process table address of new owner */
{
register struct usock *up;
struct proc *pp;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return NULLPROC;
}
pp = up->owner;
if(newowner != NULLPROC)
up->owner = newowner;
return pp;
}
/* Close down a socket three ways. Type 0 means "no more receives"; this
* replaces the incoming data upcall with a routine that discards further
* data. Type 1 means "no more sends", and obviously corresponds to sending
* a TCP FIN. Type 2 means "no more receives or sends". This I interpret
* as "abort the connection".
*/
int
shutdown(s,how)
int s; /* Socket index */
int how; /* (see above) */
{
register struct usock *up;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return -1;
}
if(up->cb.p == NULLCHAR){
errno = ENOTCONN;
return -1;
}
switch(up->type){
case TYPE_LOCAL_DGRAM:
case TYPE_LOCAL_STREAM:
if(up->cb.local->q == NULLBUF)
close_s(s);
else
up->cb.local->flags = LOC_SHUTDOWN;
break;
case TYPE_TCP:
switch(how){
case 0: /* No more receives -- replace upcall */
up->cb.tcb->r_upcall = trdiscard;
break;
case 1: /* Send EOF */
close_tcp(up->cb.tcb);
break;
case 2: /* Blow away TCB */
reset_tcp(up->cb.tcb);
up->cb.tcb = NULLTCB;
break;
}
break;
#ifdef AX25
case TYPE_AX25UI:
close_s(s);
break;
case TYPE_AX25I:
switch(how){
case 0:
case 1: /* Attempt regular disconnect */
disc_ax25(up->cb.ax25);
break;
case 2: /* Blow it away */
reset_ax25(up->cb.ax25);
up->cb.ax25 = NULLAX25;
break;
}
break;
#endif
#ifdef NETROM
case TYPE_NETROML3:
close_s(s);
break;
case TYPE_NETROML4:
switch(how){
case 0:
case 1: /* Attempt regular disconnect */
disc_nr4(up->cb.nr4);
break;
case 2: /* Blow it away */
reset_nr4(up->cb.nr4);
up->cb.nr4 = NULLNR4CB;
break;
}
break;
#endif
case TYPE_RAW:
case TYPE_UDP:
close_s(s);
break;
default:
errno = EOPNOTSUPP;
return -1;
}
psignal(up,0);
return 0;
}
/* Close a socket, freeing it for reuse. Try to do a graceful close on a
* TCP socket, if possible
*/
int
close_s(s)
int s; /* Socket index */
{
register struct usock *up;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return -1;
}
if(--up->refcnt > 0)
return 0; /* Others are still using it */
usflush(s);
if(up->ibuf != NULLBUF){
free_p(up->ibuf);
up->ibuf = NULLBUF;
}
switch(up->type){
case TYPE_LOCAL_STREAM:
case TYPE_LOCAL_DGRAM:
if(up->cb.local->peer != NULLUSOCK){
up->cb.local->peer->cb.local->peer = NULLUSOCK;
psignal(up->cb.local->peer,0);
}
free_q(&up->cb.local->q);
free(up->cb.local);
break;
case TYPE_TCP:
if(up->cb.tcb != NULLTCB){ /* In case it's been reset */
up->cb.tcb->r_upcall = trdiscard;
/* Tell the TCP_CLOSED upcall there's no more socket */
up->cb.tcb->user = -1;
close_tcp(up->cb.tcb);
}
break;
case TYPE_UDP:
if(up->cb.udp != NULLUDP){
del_udp(up->cb.udp);
}
break;
#ifdef AX25
case TYPE_AX25I:
if(up->cb.ax25 != NULLAX25){
/* Tell the TCP_CLOSED upcall there's no more socket */
up->cb.ax25->user = -1;
disc_ax25(up->cb.ax25);
}
break;
case TYPE_AX25UI:
Axui_sock = -1;
free_q(&Bcq);
psignal(&Bcq,0); /* Unblock any reads */
break;
#endif
#ifdef NETROM
case TYPE_NETROML3:
del_rnr(up->cb.rnr);
break;
case TYPE_NETROML4:
if(up->cb.nr4 != NULLNR4CB){
/* Tell the TCP_CLOSED upcall there's no more socket */
up->cb.nr4->user = -1;
disc_nr4(up->cb.nr4);
}
break;
#endif
case TYPE_RAW:
del_ip(up->cb.rip);
break;
default:
errno = EOPNOTSUPP;
return -1;
}
#ifdef LZW
if(up->zout != NULLLZW || up->zin != NULLLZW)
lzwfree(up);
#endif
free(up->name);
free(up->peername);
up->cb.p = NULLCHAR;
up->name = up->peername = NULLCHAR;
up->type = NOTUSED;
psignal(up,0); /* Wake up anybody doing an accept() or recv() */
return 0;
}
/* Increment reference count for specified socket */
int
usesock(s)
int s;
{
struct usock *up;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return -1;
}
up->refcnt++;
return 0;
}
/* Blow away all sockets belonging to a certain process. Used by killproc(). */
void
freesock(pp)
struct proc *pp;
{
register struct usock *up;
register int i;
for(i=SOCKBASE;i < Nusock+SOCKBASE;i++){
up = itop(i);
if(up != NULLUSOCK && up->type != NOTUSED && up->owner == pp)
shutdown(i,2);
}
}
/* Start of internal subroutines */
/* Raw IP receive upcall routine */
static void
rip_recv(rp)
struct raw_ip *rp;
{
psignal(itop(rp->user),1);
pwait(NULL);
}
/* UDP receive upcall routine */
static void
s_urcall(iface,udp,cnt)
struct iface *iface;
struct udp_cb *udp;
int cnt;
{
psignal(itop(udp->user),1);
pwait(NULL);
}
/* TCP receive upcall routine */
static void
s_trcall(tcb,cnt)
struct tcb *tcb;
int cnt;
{
/* Wake up anybody waiting for data, and let them run */
psignal(itop(tcb->user),1);
pwait(NULL);
}
/* TCP transmit upcall routine */
static void
s_ttcall(tcb,cnt)
struct tcb *tcb;
int cnt;
{
/* Wake up anybody waiting to send data, and let them run */
psignal(itop(tcb->user),1);
pwait(NULL);
}
/* TCP state change upcall routine */
static void
s_tscall(tcb,old,new)
struct tcb *tcb;
int old,new;
{
int s,ns;
struct usock *up,*nup,*oup;
union sp sp;
s = tcb->user;
oup = up = itop(s);
switch(new){
case TCP_CLOSED:
/* Clean up. If the user has already closed the socket,
* then up will be null (s was set to -1 by the close routine).
* If not, then this is an abnormal close (e.g., a reset)
* and clearing out the pointer in the socket structure will
* prevent any further operations on what will be a freed
* control block. Also wake up anybody waiting on events
* related to this tcb so they will notice it disappearing.
*/
if(up != NULLUSOCK){
up->cb.tcb = NULLTCB;
up->errcodes[0] = tcb->reason;
up->errcodes[1] = tcb->type;
up->errcodes[2] = tcb->code;
}
del_tcp(tcb);
break;
case TCP_SYN_RECEIVED:
/* Handle an incoming connection. If this is a server TCB,
* then we're being handed a "clone" TCB and we need to
* create a new socket structure for it. In either case,
* find out who we're talking to and wake up the guy waiting
* for the connection.
*/
if(tcb->flags.clone){
/* Clone the socket */
ns = socket(AF_INET,SOCK_STREAM,0);
nup = itop(ns);
ASSIGN(*nup,*up);
tcb->user = ns;
nup->cb.tcb = tcb;
/* Allocate new memory for the name areas */
nup->name = mallocw(SOCKSIZE);
nup->peername = mallocw(SOCKSIZE);
/* Store the new socket # in the old one */
up->rdysock = ns;
up = nup;
s = ns;
} else {
/* Allocate space for the peer's name */
up->peername = mallocw(SOCKSIZE);
/* Store the old socket # in the old socket */
up->rdysock = s;
}
/* Load the addresses. Memory for the name has already
* been allocated, either above or in the original bind.
*/
sp.p = up->name;
sp.in->sin_family = AF_INET;
sp.in->sin_addr.s_addr = up->cb.tcb->conn.local.address;
sp.in->sin_port = up->cb.tcb->conn.local.port;
up->namelen = SOCKSIZE;
sp.p = up->peername;
sp.in->sin_family = AF_INET;
sp.in->sin_addr.s_addr = up->cb.tcb->conn.remote.address;
sp.in->sin_port = up->cb.tcb->conn.remote.port;
up->peernamelen = SOCKSIZE;
/* Wake up the guy accepting it, and let him run */
psignal(oup,1);
pwait(NULL);
break;
default: /* Ignore all other state transitions */
break;
}
psignal(up,0); /* In case anybody's waiting */
}
/* Discard data received on a TCP connection. Used after a receive shutdown or
* close_s until the TCB disappears.
*/
static void
trdiscard(tcb,cnt)
struct tcb *tcb;
int cnt;
{
struct mbuf *bp;
recv_tcp(tcb,&bp,(int16)cnt);
free_p(bp);
}
#ifdef AX25
/* AX.25 receive upcall */
void
s_arcall(axp,cnt)
struct ax25_cb *axp;
int cnt;
{
int ns;
struct usock *up,*nup,*oup;
union sp sp;
up = itop(axp->user);
/* When AX.25 data arrives for the first time the AX.25 listener
is notified, if active. If the AX.25 listener is a server its
socket is duplicated in the same manner as in s_tscall().
*/
if (Axi_sock != -1 && axp->user == -1) {
oup = up = itop(Axi_sock);
/* From now on, use the same upcalls as the listener */
axp->t_upcall = up->cb.ax25->t_upcall;
axp->r_upcall = up->cb.ax25->r_upcall;
axp->s_upcall = up->cb.ax25->s_upcall;
if (up->cb.ax25->flags.clone) {
/* Clone the socket */
ns = socket(AF_AX25,SOCK_STREAM,0);
nup = itop(ns);
ASSIGN(*nup,*up);
axp->user = ns;
nup->cb.ax25 = axp;
/* Allocate new memory for the name areas */
nup->name = mallocw(sizeof(struct sockaddr_ax));
nup->peername = mallocw(sizeof(struct sockaddr_ax));
/* Store the new socket # in the old one */
up->rdysock = ns;
up = nup;
} else {
axp->user = Axi_sock;
del_ax25(up->cb.ax25);
up->cb.ax25 = axp;
/* Allocate space for the peer's name */
up->peername = mallocw(sizeof(struct sockaddr_ax));
/* Store the old socket # in the old socket */
up->rdysock = Axi_sock;
}
/* Load the addresses. Memory for the name has already
* been allocated, either above or in the original bind.
*/
sp.p = up->name;
sp.ax->sax_family = AF_AX25;
memcpy(sp.ax->ax25_addr,axp->local,AXALEN);
memcpy(sp.ax->iface,axp->iface->name,ILEN);
up->namelen = sizeof(struct sockaddr_ax);
sp.p = up->peername;
sp.ax->sax_family = AF_AX25;
memcpy(sp.ax->ax25_addr,axp->remote,AXALEN);
memcpy(sp.ax->iface,axp->iface->name,ILEN);
up->peernamelen = sizeof(struct sockaddr_ax);
/* Wake up the guy accepting it, and let him run */
psignal(oup,1);
pwait(NULL);
return;
}
/* Wake up anyone waiting, and let them run */
psignal(up,1);
pwait(NULL);
}
/* AX.25 transmit upcall */
void
s_atcall(axp,cnt)
struct ax25_cb *axp;
int cnt;
{
/* Wake up anyone waiting, and let them run */
psignal(itop(axp->user),1);
pwait(NULL);
}
/* AX25 state change upcall routine */
void
s_ascall(axp,old,new)
register struct ax25_cb *axp;
int old,new;
{
int s;
struct usock *up;
s = axp->user;
up = itop(s);
switch(new){
case LAPB_DISCONNECTED:
/* Clean up. If the user has already closed the socket,
* then up will be null (s was set to -1 by the close routine).
* If not, then this is an abnormal close (e.g., a reset)
* and clearing out the pointer in the socket structure will
* prevent any further operations on what will be a freed
* control block. Also wake up anybody waiting on events
* related to this block so they will notice it disappearing.
*/
if(up != NULLUSOCK){
up->errcodes[0] = axp->reason;
up->cb.ax25 = NULLAX25;
}
del_ax25(axp);
break;
default: /* Other transitions are ignored */
break;
}
psignal(up,0); /* In case anybody's waiting */
}
#endif
#ifdef NETROM
/* NET/ROM receive upcall routine */
static void
s_nrcall(cb,cnt)
struct nr4cb *cb;
int cnt;
{
/* Wake up anybody waiting for data, and let them run */
psignal(itop(cb->user),1);
pwait(NULL);
}
/* NET/ROM transmit upcall routine */
static void
s_ntcall(cb,cnt)
struct nr4cb *cb;
int cnt;
{
/* Wake up anybody waiting to send data, and let them run */
psignal(itop(cb->user),1);
pwait(NULL);
}
/* NET/ROM state change upcall routine */
static void
s_nscall(cb,old,new)
struct nr4cb *cb;
int old,new;
{
int s,ns;
struct usock *up,*nup,*oup;
union sp sp;
s = cb->user;
oup = up = itop(s);
if(new == NR4STDISC && up != NULLUSOCK){
/* Clean up. If the user has already closed the socket,
* then up will be null (s was set to -1 by the close routine).
* If not, then this is an abnormal close (e.g., a reset)
* and clearing out the pointer in the socket structure will
* prevent any further operations on what will be a freed
* control block. Also wake up anybody waiting on events
* related to this cb so they will notice it disappearing.
*/
up->cb.nr4 = NULLNR4CB;
up->errcodes[0] = cb->dreason;
}
if(new == NR4STCON && old == NR4STDISC){
/* Handle an incoming connection. If this is a server cb,
* then we're being handed a "clone" cb and we need to
* create a new socket structure for it. In either case,
* find out who we're talking to and wake up the guy waiting
* for the connection.
*/
if(cb->clone){
/* Clone the socket */
ns = socket(AF_NETROM,SOCK_SEQPACKET,0);
nup = itop(ns);
ASSIGN(*nup,*up);
cb->user = ns;
nup->cb.nr4 = cb;
cb->clone = 0; /* to avoid getting here again */
/* Allocate new memory for the name areas */
nup->name = mallocw(sizeof(struct sockaddr_nr));
nup->peername = mallocw(sizeof(struct sockaddr_nr));
/* Store the new socket # in the old one */
up->rdysock = ns;
up = nup;
s = ns;
} else {
/* Allocate space for the peer's name */
up->peername = mallocw(sizeof(struct sockaddr_nr));
/* Store the old socket # in the old socket */
up->rdysock = s;
}
/* Load the addresses. Memory for the name has already
* been allocated, either above or in the original bind.
*/
sp.p = up->name;
sp.nr->nr_family = AF_NETROM;
ASSIGN(sp.nr->nr_addr,up->cb.nr4->local);
up->namelen = sizeof(struct sockaddr_nr);
sp.p = up->peername;
sp.nr->nr_family = AF_NETROM;
ASSIGN(sp.nr->nr_addr,up->cb.nr4->remote);
up->peernamelen = sizeof(struct sockaddr_nr);
/* Wake up the guy accepting it, and let him run */
psignal(oup,1);
pwait(NULL);
}
/* Ignore all other state transitions */
psignal(up,0); /* In case anybody's waiting */
}
#endif
/* Verify address family and length according to the socket type */
static int
checkaddr(type,name,namelen)
int type;
char *name;
int namelen;
{
union sp sp;
sp.p = name;
/* Verify length and address family according to protocol */
switch(type){
case TYPE_TCP:
case TYPE_UDP:
if(sp.in->sin_family != AF_INET
|| namelen != sizeof(struct sockaddr_in))
return -1;
break;
#ifdef AX25
case TYPE_AX25I:
case TYPE_AX25UI:
if(sp.ax->sax_family != AF_AX25
|| namelen != sizeof(struct sockaddr_ax))
return -1;
break;
#endif
#ifdef NETROM
case TYPE_NETROML3:
case TYPE_NETROML4:
if(sp.nr->nr_family != AF_NETROM
|| namelen != sizeof(struct sockaddr_nr))
return -1;
break;
#endif
}
return 0;
}
/* Issue an automatic bind of a local address */
static void
autobind(s,af)
int s,af;
{
char buf[MAXSOCKSIZE];
union sp sp;
sp.p = buf;
switch(af){
case AF_INET:
sp.in->sin_family = AF_INET;
sp.in->sin_addr.s_addr = INADDR_ANY;
sp.in->sin_port = Lport++;
bind(s,sp.p,sizeof(struct sockaddr_in));
break;
#ifdef AX25
case AF_AX25:
sp.ax->sax_family = AF_AX25;
memset(sp.ax->ax25_addr,'\0',AXALEN);
memset(sp.ax->iface,'\0',ILEN);
bind(s,sp.p,sizeof(struct sockaddr_ax));
break;
#endif
#ifdef NETROM
case AF_NETROM:
sp.nr->nr_family = AF_NETROM;
memcpy(sp.nr->nr_addr.user,Mycall,AXALEN);
memcpy(sp.nr->nr_addr.node,Mycall,AXALEN);
bind(s,sp.p,sizeof(struct sockaddr_nr));
break;
#endif
}
}
/* Return a pair of mutually connected sockets in sv[0] and sv[1] */
int
socketpair(af,type,protocol,sv)
int af;
int type;
int protocol;
int sv[];
{
struct usock *up0, *up1;
if(sv == NULLINT){
errno = EFAULT;
return -1;
}
if(af != AF_LOCAL){
errno = EAFNOSUPPORT;
return -1;
}
if(type != SOCK_STREAM && type != SOCK_DGRAM){
errno = ESOCKTNOSUPPORT;
return -1;
}
if((sv[0] = socket(af,type,protocol)) == -1)
return -1;
if((sv[1] = socket(af,type,protocol)) == -1){
close_s(sv[0]);
return -1;
}
up0 = itop(sv[0]);
up1 = itop(sv[1]);
up0->cb.local->peer = up1;
up1->cb.local->peer = up0;
return sv[1];
}